#include <time.h>
#include <winsock2.h>
#include "MySQL/mysql.h"

#include "CSemaphore.h"
#include "CPacket.h"
#include "CLuaManager.h"
#include "CBlowfish.h"
#include "CZlib.h"
#include "CActionHandler.h"
#include "CUtil.h"
#include "CDb.h"
#include "CItem.h"
#include "CCharStatsPacket.h"
#include "CCharSkillPacket.h"
#include "CCSPositionPacket.h"
#include "CPositionPacket.h"
#include "CReleasePacket.h"
#include "CTradeContainer.h"
#include "CTradeActionPacket.h"
#include "CTransportHandler.h"
#include "CGlobal.h"
#include "CDefinePartyPacket.h"
#include "CLinkshell.h"
#include "CPartySearchPacket.h"
#include "CCharPacket.h"
#include "CItemContainer.h"
#include "Utils/CProfilingWindow.h"
#include "Utils/listKeyClass.h"
#include "CEquipPacket.h"
#include "CInventoryAssignPacket.h"
#include "CCharJobInfoPacket.h"
#include "CDownloadingUnknownPacket.h"
#include "CUpdateCharPacket.h"
#include "CDownloadingStopPacket.h"
#include "CCharGeneralInfoPacket.h"

#define _MAX_PACKET_SIZE 3072
//4096
#define _MAX_PACKET_BUFFER_SIZE 8192
//8192

int CPacket::makePacketsReadable(char * recvbuf, int recvSize, CChar * schar, struct Settings * s) {
	unsigned short tmp, i;
	tmp = (recvSize-0x1c)/4;
	tmp -= tmp%2;

	//check if the clientdata is locked
	bool retVal = schar->tryLockAttributes();

	if(!retVal) {
		CConsole::outErr("Character data locked because of zoning (Char %s)",schar->getName());
		return 1;
	}

	for(i = 0; i < tmp; i += 2) {
		CBlowfish::decipher((unsigned long *)recvbuf+i+7, (unsigned long *)recvbuf+i+8, schar->enc.P, schar->enc.S[0]);
	}

	unsigned char hash[16];
	md5((unsigned char*)(recvbuf+0x1c), hash, recvSize-0x2c);

	if(memcmp(hash, recvbuf+recvSize-16, 16)) {
		s->badpackets++;
		CConsole::outErr("Unable to decipher packet!");
		schar->unlockAttributes();
		return 1;
	} else {
		s->goodpackets++;
	}

	schar->unlockAttributes();
	
	return 0;
}

int CPacket::isUnencrypted(char * recvbuf, int recvSize){
	unsigned char hash[16];

	if(recvSize == -1) {
		return 2;
	}

	md5((unsigned char *)(recvbuf+0x1c), hash, recvSize-0x2c);
	if(!memcmp(hash, recvbuf+recvSize-16, 16)) {
		return 1;
	}
	
	return 0;
}

int packetlistpacketsMax = 0;
int commlistpacketsMax = 0;
int compressMax = 0;
int spawnMobMax = 0;
int spawnNpcMax = 0;

int CPacket::genGamePacket(char * output, char * recvbuf, union PacketData pd, unsigned long recvSize, char special, unsigned long * compressTable, CChar * schar){

	unsigned long size = 0, j = 0, k, s1 = 0;
	unsigned char buf[_MAX_PACKET_BUFFER_SIZE];
	char * end;
	long long now;
	int diff;

	unsigned short * code = (unsigned short *)output;
	unsigned short ct;

	memcpy(output, recvbuf, 0x1c);

	ct = pd.us[1];
	union PacketData pd2;
	pd2.c = recvbuf;

	// Update the character

	schar->lastUpdate = (unsigned long long) clock();

	bool sendInventory = false;
	
	if(special) {	// actually zone
		
		schar->zoner();
	
		if (schar->zoning == 1) {
			CConsole::outTime("Character Key needs to be updated.");
			schar->changeZone();
		} else if (schar->zoning != 2) {
			CConsole::outErr("ZONING WAS AT STATE %d BUT SHOULD BE STATE 2.",schar->zoning);
		}
		schar->m_zoneHack ++;
		if(schar->m_zoneHack > 3 && schar->zoning == 2) {
			schar->zoning = 1;
			schar->m_zoneHack = 0;
			schar->packetList->clear();
			schar->zoner();
		}
		
		schar->packetList->lock();
		CBasicPacket* currPacket = NULL;
		while(schar->packetList->getSize()>0) {
			currPacket = schar->packetList->at(0);

			if ((!sendInventory) && ((size + ( currPacket->getSize()*2) ) > _MAX_PACKET_SIZE)) {
				CConsole::outErr("genGamePacket Overflow! newSize: %i, Char: %s", size, schar->getName());//this should not happen
				break;
			}

			if ((!sendInventory) && ((currPacket->getType() == 0x20) || (currPacket->getType() == 0x1E) || (currPacket->getType() == 0x1F))) {
				sendInventory = true;
			}

			if ((sendInventory) && (currPacket->getType() == 0x1D)) {
				sendInventory = false;
			}

		

			size += CPacket::sendPremadeCPacket((buf+size),  code[0], currPacket);
			//CConsole::outDebOnly("Preparing packet %x", schar->packetList.at(i).getType());

			schar->packetList->removeAt(0);

			delete currPacket;
		}
		schar->packetList->unlock();

		goto compress1;
	}

	// die :/
	if((schar->getHp() <= 0) && (schar->getAnimation() != 0x03)) {
		schar->h.hp = 0;
		schar->die();

		goto compress1;	
	}

	schar->lockRecastSpellList();
	for(unsigned int l = 0; l < schar->recastSpellList.size(); l++) {
		if((schar->recastSpellList.at(l).timeStamp + schar->recastSpellList.at(l).recast) < (unsigned int)time(NULL)) {
			schar->recastSpellList.erase(schar->recastSpellList.begin()+l,schar->recastSpellList.begin()+l+1);
			break;
		}
	}
	schar->unlockRecastSpellList();

	schar->lockRecastAbilityList();
	for(unsigned int i = 0; i < schar->recastAbilityList.size(); i++) {
		int ttg = (schar->recastAbilityList.at(i).recast + schar->recastAbilityList.at(i).timeStamp) - time(NULL);
		if(ttg < 0) {
			schar->recastAbilityList.erase(schar->recastAbilityList.begin()+i);

			break;
		}
	}
	schar->unlockRecastAbilityList();

	for(end = pd.c + recvSize - 4; pd.c <= end && pd.us[1] == ct && pd.uc[1]; pd.c += (unsigned long)pd.uc[1]*2) {
		if(pd.uc[0] != 0x15) {
			CConsole::outTime("Incoming Packet: %02hX | %02hX from user: %s", (unsigned short)pd.uc[0],(unsigned short)pd.uc[1], schar->m_name);

			char message[256];

			sprintf(message,"\n "); //linebreak for first packet line
			for(int y=0; y < ((unsigned short)pd.uc[1])*2; y++) {
				sprintf(message,"%s %02hx ",message,(unsigned short)pd.uc[y]);
				if(((y+1)%16) == 0) {
					CConsole::outPlain(message);
					sprintf(message,"");
				}
			}
			if (strlen(message)>0) {
				CConsole::outPlain(message);
				sprintf(message,"");
			}
			//CConsole::outPlain("\n");
		}
		union PacketData pdd;
		pdd.c = (char*)buf+size;

		switch(pd.us[0] & 0x1ff){
			case 0x0b:	// actualy logout
				now = clock();
				size += schar->logout(buf+size, code[0], false);
				diff = clock() - now;
				CProfilingWindow::getInstance()->setValue("genGamePacket::logout(0x0b)", "%d", diff);
				
				break;
			case 0x0f:
				schar->sendLevel();
				schar->quePacket(CDownloadingStopPacket());
				size += schar->stopDld(buf+size, code[0]);

				break;
			case 0x0c:	// load inventory
			
				schar->quePacket(CDownloadingUnknownPacket());
				schar->updateInvSize();
				// send all inventory and storages to client
	//			schar->sendInvStorAll();
				schar->sendFinishInvStor();
				schar->quePacket(CCharJobInfoPacket(schar));
				schar->quePacket(CEquipPacket(schar->equip[SLOT_LINKSHELL]));
				schar->updateKeyItems();
				schar->quePacket(CDownloadingStopPacket());
				size += schar->stopDld(buf+size, code[0]);
				schar->sendQuestMissionLog();
				schar->updateSpells();
				schar->updateAbilities();
					
				schar->sendLevel();
		
				if (schar->onTransport > 0) {
					CTransportHandler::spawnDock(schar);
					size += schar->reqChar(buf+size, pd.c, code[0]);
					schar->onTransport = 0;
				}
	
				schar->quePacket(CUpdateCharPacket(schar));
				schar->quePacket(CCharGeneralInfoPacket(schar));

				schar->updateHealth();
				
				if (schar->inParty > 0) {
					schar->insertPacket(CHAR_INALLIANCE_SELF,CDefinePartyPacket(schar->getParty()));
				}
				schar->updateChar();

				schar->updateInvSize();
				// send all inventory and storages to client
				schar->sendInvStorAll();
				schar->sendFinishInvStor();
			
				//goto compress1;
				break;
			case 0x0d:
				now = clock();
			
				if (schar->m_logoutType == 0x00) {
				//	schar->packetList->lock();
				//	schar->packetList->clear();
				//	schar->changeZone();
				//	schar->packetList->unlock();

				} else {
					size+= schar->logout(buf+size, code[0], true);
				}
				diff = clock() - now;
				CProfilingWindow::getInstance()->setValue("genGamePacket::changeZone(0x0d)", "%d", diff);
				
				break;
			case 0x11: // Done zoning?
				schar->zoning = 0;
				for(int i=0; i<16; i++) {
					schar->quePacket(CEquipPacket(schar->equip[i],i));
				}
		//		schar->updateHealth();
		//		schar->updateChar();
				
				break;
			case 0x15:	// StdClient
				now = clock();
				schar->updatePos(pd);
				schar->spawnPCs();
				CZoneHandler::getInstance()->spawnNpcs(schar);
				diff = clock() - now;
				spawnNpcMax = max(spawnNpcMax,diff);
				CProfilingWindow::getInstance()->setValue("genGamePacket::StdClient(0x15)-spawnNpcs", "%d - Max: %d", diff, spawnNpcMax);
				now = clock();
				size += CZoneHandler::getInstance()->spawnMobs((unsigned char *)(buf+size), code[0], schar);
				diff = clock() - now;
				spawnMobMax = max(spawnMobMax,diff);
				CProfilingWindow::getInstance()->setValue("genGamePacket::StdClient(0x15)-spawnMobs", "%d - Max: %d", diff, spawnMobMax);
				break;
			case 0x1a:	// NPC interaction
				now = clock();
				size += CActionHandler::genActionPacket(schar, buf+size, pd.c, code[0]);
				diff = clock() - now;
				CProfilingWindow::getInstance()->setValue("genGamePacket::NPC interaction(0x1a)", "%d", diff);
				break;
			case 0x16:	// request Entity
				now = clock();
				size += schar->reqChar(buf+size, pd.c, code[0]);
				diff = clock() - now;
				CProfilingWindow::getInstance()->setValue("genGamePacket::request Entity(0x16)", "%d", diff);
				break;
			case 0x28: { //Remove item from inventory
				now = clock();
				unsigned long quantity;
				unsigned char slotID;
				memcpy(&quantity, pd.c+0x04, 4);
				memcpy(&slotID, pd.c+0x09, 1);
				schar->removeItem(slotID, LOC_INVENTORY, quantity);
				schar->sendFinishInvStor();
				diff = clock() - now;
				CProfilingWindow::getInstance()->setValue("genGamePacket::Remove item(0x28)", "%d", diff);
				break;
			}
			case 0x29: { //Moving items between storage and inventory slots
				now = clock();
				unsigned long quantity;
				unsigned char fromLocationID;
				unsigned char fromSlotID;
				unsigned char toLocationID;
				unsigned char toSlotID;
				memcpy(&quantity, pd.c+0x04, 4);
				memcpy(&fromLocationID, pd.c+0x08, 1);
				memcpy(&toLocationID, pd.c+0x09, 1);
				memcpy(&fromSlotID, pd.c+0x0A, 1);
				memcpy(&toSlotID, pd.c+0x0B, 1);
				CConsole::outDebOnly("From location %i slot %i. To location %i slot %i. Quantity: %i",fromLocationID, fromSlotID, toLocationID, toSlotID, quantity);
				schar->moveItem(fromLocationID, fromSlotID, toLocationID, toSlotID, quantity);
				diff = clock() - now;
				CProfilingWindow::getInstance()->setValue("genGamePacket::move item(0x29)", "%d", diff);
				break;
			}
			case 0x32: { //Trade request
				now = clock();
				unsigned long charId;
				unsigned short targId;
				memcpy(&charId, pd.c+0x04, 4);
				memcpy(&targId, pd.c+0x08, 2);
				CConsole::outDebOnly("Trade request from CharID: %d TargetID: %d", charId, targId);
				schar->sendTradeRequest(targId);
				diff = clock() - now;
				CProfilingWindow::getInstance()->setValue("genGamePacket::trade request(0x32)", "%d", diff);
				break;
			}
			case 0x33: { //Accept/Cancel request, Accept actual trade
				now = clock();
				unsigned long action;
				memcpy(&action, pd.c+0x04, 4);
				schar->sendTradeAction(action);
				diff = clock() - now;
				CProfilingWindow::getInstance()->setValue("genGamePacket::trade action(0x33)", "%d", diff);
				break;
			}
			case 0x34: { //Send item to server
				now = clock();
				unsigned long quantity;
				unsigned short itemID;
				unsigned char invSlotID;
				unsigned char tradeSlotID;
				memcpy(&quantity, pd.c+0x04, 4);
				memcpy(&itemID, pd.c+0x08, 2);
				memcpy(&invSlotID, pd.c+0x0A, 1);
				memcpy(&tradeSlotID, pd.c+0x0B, 1);

				schar->sendTradeItem(itemID, quantity, invSlotID, tradeSlotID);
				diff = clock() - now;
				CProfilingWindow::getInstance()->setValue("genGamePacket::send trade item(0x34)", "%d", diff);
				break;
			}
			case 0x36: 
				{  // Trade with NPC
					now = clock();
					// New container for items in trade window
					CTradeContainer * tradeItems = new CTradeContainer;
					tradeItems->setOwner(schar);
					unsigned char numItems = pd.uc[0x3C];
					for(unsigned char tradeSlot = 1;tradeSlot <= numItems; tradeSlot++) {
						unsigned long itemQuantity;
						unsigned char invSlotID;
						
						memcpy(&itemQuantity,pd.c+0x04+(tradeSlot*0x04),4);
						invSlotID = pd.uc[0x2F+tradeSlot];

						if(invSlotID == 0) {
							// inventory slot 0 = gil
							if(schar->getGil() >= itemQuantity) {
								tradeItems->setGil(itemQuantity);
							}
						} else {
							unsigned char slotID = tradeSlot;
							if(tradeItems->getGil() > 0)
								slotID -= 1;
							CItem * charInvItem = schar->getItem(LOC_INVENTORY, invSlotID);
							if (charInvItem != NULL) {
								if(charInvItem->getQuantity() >= itemQuantity) {
									unsigned short itemID = charInvItem->getId();
									tradeItems->setItemID(itemID, slotID);
									tradeItems->setInvSlotID(invSlotID, slotID);
									tradeItems->setQuantity(itemQuantity, slotID);
								}
							}
						}
					}
					schar->sendTradeNPC(tradeItems);
					diff = clock() - now;
					CProfilingWindow::getInstance()->setValue("genGamePacket::trade npc(0x36)", "%d", diff);
				}
				break;
			case 0x37: { //Using an item
				now = clock();
				unsigned char slotID;
				memcpy(&schar->m_itemTarget,pd.s+2,4);
				memcpy(&slotID, pd.c+0x0E, 1);
				schar->useItem(slotID);
				diff = clock() - now;
				CProfilingWindow::getInstance()->setValue("genGamePacket::use item(0x37)", "%d", diff);
				break;
			}
			case 0x3A: { //Sorts the players inventory
				now = clock();
				unsigned char locationID;
				memcpy(&locationID, pd.c+0x04, 1);
				schar->sortInvStor(locationID);
				diff = clock() - now;
				CProfilingWindow::getInstance()->setValue("genGamePacket::sort inv(0x3a)", "%d", diff);
				break;
			}
			case 0x41: { //Cast a lot on the treasure pool item
				now = clock();
				unsigned short lot;
				unsigned char slotID;
				memcpy(&slotID,pd.c+0x04,1);
				lot = 1+(int)(rand()%1000);
				schar->getTreasurePool()->lotItem(schar,slotID,lot);
				diff = clock() - now;
				CProfilingWindow::getInstance()->setValue("genGamePacket::cast lot(0x41)", "%d", diff);
				break;
			}
			case 0x42: { //Cast a lot on the treasure pool item
				now = clock();
				unsigned short lot;
				unsigned char slotID;
				memcpy(&slotID,pd.c+0x04,1);
				lot = -1;
				schar->getTreasurePool()->lotItem(schar,slotID,lot);
				diff = clock() - now;
				CProfilingWindow::getInstance()->setValue("genGamePacket::cast lot(0x42)", "%d", diff);
				break;
			}
			case 0x4B:
				now = clock();
				schar->sendServerMessage();
				if (!schar->lsLoginMessageSent) {
					CLinkshell::sendLsMsg(schar);
				}
				diff = clock() - now;
				CProfilingWindow::getInstance()->setValue("genGamePacket::send login message(0x4b)", "%d", diff);
				break;
			case 0x50: {
				now = clock();
				unsigned char invSlotID = 0x00;
				unsigned char equipSlotID = 0x00;
				memcpy(&invSlotID,(pd.c+4),1);
				memcpy(&equipSlotID,(pd.c+5),1);
				schar->equipItem(invSlotID, equipSlotID);
				diff = clock() - now;
				CProfilingWindow::getInstance()->setValue("genGamePacket::equip item(0x50)", "%d", diff);
				break;
			}
			case 0x59: { // Synth complete
				now = clock();
				schar->sendSynthDone();
				diff = clock() - now;
				CProfilingWindow::getInstance()->setValue("genGamePacket::complete synth(0x59)", "%d", diff);
				break;
			}
			case 0x5D:
				now = clock();
				size += schar->emote(buf+size, pd.c, code[0]);
				diff = clock() - now;
				CProfilingWindow::getInstance()->setValue("genGamePacket::emote(0x5D)", "%d", diff);
				break;
			case 0x5A:
			
				break;
			case 0x5E:	// allow to zone
				now = clock();
				size += schar->zoneLine(pd);
				size += schar->chip(buf+size, code[0]);
				diff = clock() - now;
				CProfilingWindow::getInstance()->setValue("genGamePacket::allow to zone(0x5e)", "%d", diff);
				break;
			case 0x5B:	{  // Event finished
				now = clock();
				// Get event id information from client packet
				int eventid = 0;
				int menuChoice = 0;
				memcpy(&eventid,pd.c+18,2);
				memcpy(&menuChoice,pd.c+8,4);

				// Char on a transport
				if(schar->onTransport > 0) {
					CTransportHandler::zoneChar(schar);
				}

				// Char not on a transport, run lua script onEventFinish
				else {
					unsigned char eventNotDone;
					memcpy(&eventNotDone,pd.c+0x0e,1);
					if(eventNotDone == 1) {
						CLuaManager::OnEventSelection( schar, eventid, menuChoice);
					} else {
						CLuaManager::OnEventFinish( schar, eventid, menuChoice);
					}
				}

				// inEvent set to true for proper packet generation.
				schar->inEvent = true;

				// release() sets inEvent to false.
				schar->release();
				diff = clock() - now;
				CProfilingWindow::getInstance()->setValue("genGamePacket::event finished(0x5b)", "%d", diff);
				break;
			}
			case 0x5C:	{
				now = clock();
				int eventid = 0;
				int menuChoice = 0;
				memcpy(&eventid, pd.c+26, 2);

				memcpy(&schar->loc.p.x, pd.c+4, 4);
				memcpy(&schar->loc.p.y, pd.c+8, 4);
				memcpy(&schar->loc.p.z, pd.c+12, 4);
				memcpy(&schar->loc.p.rotation, pd.c+31, 1);

				unsigned char eventNotDone;
				memcpy(&eventNotDone,pd.c+0x1e,1);
				if(eventNotDone == 1) {
					CLuaManager::OnEventSelection( schar, eventid, menuChoice);
				} else {
					CLuaManager::OnEventFinish( schar, eventid, menuChoice);
				}

				schar->quePacket(CCSPositionPacket(schar,schar->getPos()));
				schar->quePacket(CPositionPacket(schar,schar->getPos()));
				schar->inEvent = true;
				schar->release();
				diff = clock() - now;
				CProfilingWindow::getInstance()->setValue("genGamePacket::event finished(0x5c)", "%d", diff);
				break;
			}
			case 0x61:
				now = clock();
				schar->updateHealth();
			//	schar->updateChar();
				schar->quePacket(CCharStatsPacket(schar));
				schar->quePacket(CCharSkillPacket(schar));
				diff = clock() - now;
				CProfilingWindow::getInstance()->setValue("genGamePacket::(0x61)", "%d", diff);
									
					
				
				break;
			case 0x64: {
					now = clock();
					int keyTable = 0;
					keyTable = pd.uc[0x4A];
					memcpy((schar->k.seenList+(0x40*keyTable)),pd.uc+8,0x40);
					diff = clock() - now;
					CProfilingWindow::getInstance()->setValue("genGamePacket::(0x64)", "%d", diff);
					break;
				}
			case 0x6E: {
					now = clock();
					int charid = 0;
					memcpy(&charid,(pd.c+4),4);

					CChar * invitee = CChar::getCChar(charid,"",0);
					if(invitee != NULL) {
						schar->sendPartyInvite(invitee);
					} else {
						CConsole::outTime("invitee is null..");
					}
					diff = clock() - now;
					CProfilingWindow::getInstance()->setValue("genGamePacket::send party inv(0x6e)", "%d", diff);
					break;
				}
			case 0x6F: //Leave Party
					now = clock();
					//Check to make sure the person trying to remove the player is in a party
					if ( (schar->getParty() != NULL) ) {
						//Remove the member and do the party updates
						schar->getParty()->removeMember(schar);
					}
					diff = clock() - now;
					CProfilingWindow::getInstance()->setValue("genGamePacket::leave party(0x6f)", "%d", diff);
					break;
			case 0x70: //disband Party
					now = clock();
					if (schar->getParty() != NULL) {
						schar->getParty()->startDisband(schar);
					}
					diff = clock() - now;
					CProfilingWindow::getInstance()->setValue("genGamePacket::disband party(0x70)", "%d", diff);
					break;
			case 0x71: 
				{ 
					now = clock();
					char dummyname[16];
					memset(dummyname, 0, 16);
					memcpy(&dummyname,(pd.c+0x0C),16);

					CConsole::outDebOnly("Remove char %s from %s(%d)",dummyname,pd.c[10]==1?"LS":"PARTY", pd.c[10]);
					//Find the character to remove
					CChar * dummychar = CChar::getCChar(0, dummyname, 0);

					//Verify we found the character
					if ( dummychar == NULL ) {
						break;
					}

					// Remove char from Party
					if (pd.c[10]==0) {
						//Check to make sure the person trying to remove the player is in a party
						if ( (schar->getParty() != NULL) && (dummychar->getParty() == schar->getParty()) ) {
							//One last check, make sure they are the party leader.
							if (schar->getParty()->getLeader() == schar) {
								schar->getParty()->removeMember(dummychar);
							}
						}
					} else if (pd.c[10]==1) { //Linkshell 
						//Refuse if the sender has no active shell
						if (schar->getLinkshell()==NULL) {
							break;
						}

						//Break the linkshellItems
						CLinkshell * linkshell;
						linkshell = schar->getLinkshell();
						if(linkshell != NULL) {
							CLinkshell::breakLinkshellItems(dummychar,linkshell->getLsId());
						}
					} else {
						CConsole::outDeb("Remove from packet with unknown 11th byte: %.2X",pd.c[10]);
					}
					diff = clock() - now;
					CProfilingWindow::getInstance()->setValue("genGamePacket::remove char from xx(0x71)", "%d", diff);
					break;
				}
			case 0x74: { //Accept or decline invite
					now = clock();
					int result = 0;
					memcpy(&result,(pd.c+4),1);
					schar->partyInviteHandler(result);
					diff = clock() - now;
					CProfilingWindow::getInstance()->setValue("genGamePacket::accept/decline invite(0x74)", "%d", diff);
					break;
				}
			case 0x76: //Request fellow party members stats
					now = clock();
					if ((schar->inParty != 0) && (schar->getParty() != NULL)) {
						size += schar->getParty()->reloadParty(schar,false);
					}
					diff = clock() - now;
					CProfilingWindow::getInstance()->setValue("genGamePacket::request pt status(0x76)", "%d", diff);
					break;
			case 0x77: 
				{ //Changing Quarter Master / Changing Leader / Changing linksack/pearl to linkpearl/sack
					now = clock();
					unsigned char changeType;
					char dummyname[16];
					memset(dummyname, 0, 16);
					memcpy(&dummyname,(pd.c+0x04),16);
					changeType = pd.uc[21];
					//Find the character
					CChar * dummychar = CChar::getCChar(0, dummyname, 0);
					if (dummychar == NULL) {
						CConsole::outErr("Char not found for %s operation!",(pd.uc[20] == 0)?"party":"linkshell");
						break;
					}
					if (pd.uc[20] == 0) { //Party operation
						//Verify we found the character
						if (schar->getParty() != NULL) {
							schar->getParty()->assignPartyRole(schar,dummychar,changeType);
						}
					} else if (pd.uc[20] == 1) { //Linkshell Operation
						if (dummychar->getLinkshell()!=schar->getLinkshell()) {
							break;
						}

						CLinkshell::modifyLinkshellItems(dummychar, changeType);
					}
					diff = clock() - now;
					CProfilingWindow::getInstance()->setValue("genGamePacket::linkshell/party leadership(0x77)", "%d", diff);
					break;
				}
			case 0x78: //Party Search
				now = clock();
				schar->quePacket(CPartySearchPacket(schar));
				diff = clock() - now;
				CProfilingWindow::getInstance()->setValue("genGamePacket::linkshell/party search(0x78)", "%d", diff);
				break;
			case 0x83: { // Buy item
				now = clock();
				unsigned long quantity = 0;
				unsigned char shopSlotID = 0x00;
				memcpy(&quantity,(pd.c+4),4);
				memcpy(&shopSlotID,(pd.c+10),1);
				schar->buyShop(shopSlotID, quantity);
				diff = clock() - now;
				CProfilingWindow::getInstance()->setValue("genGamePacket::buy(0x83)", "%d", diff);
				break;
			}
			case 0x84: { // Sell/Appraise item
				now = clock();
				unsigned short itemID = 0;
				unsigned char invSlotID = 0x00;
				unsigned long quantity = 0;
				memcpy(&quantity,(pd.c+4),4);
				memcpy(&itemID,(pd.c+8),2);
				memcpy(&invSlotID,(pd.c+10),1);
				schar->appraiseShop(invSlotID, itemID, quantity);
				diff = clock() - now;
				CProfilingWindow::getInstance()->setValue("genGamePacket::appraise(0x84)", "%d", diff);
				break;
			}
			case 0x85: { // Client tells server to sell. Info from 0x84
				now = clock();
				schar->sellShop();
				diff = clock() - now;
				CProfilingWindow::getInstance()->setValue("genGamePacket::sell(0x85)", "%d", diff);
				break;
			}
			case 0x96: { // Synth
				now = clock();
				unsigned short crystalItemID = 0x0000;
				unsigned char crystalInvSlotID = 0x00;
				unsigned char itemCount = 0x00;
				memcpy(&crystalItemID,(pd.c+6),2);
				memcpy(&crystalInvSlotID,(pd.c+8),1);
				memcpy(&itemCount,(pd.c+9),1);

				CTradeContainer * synthContainer = new CTradeContainer;

				for(unsigned char itemNum = 1; itemNum <= 8; itemNum++) {
					if(itemNum <= itemCount) {
						unsigned short itemID = 0;
						unsigned char invSlotID = 0;

						memcpy(&itemID,(pd.c+8+(2*itemNum)),2);
						memcpy(&invSlotID,(pd.c+25+itemNum),1);

						synthContainer->setItemID(itemID, itemNum);
						synthContainer->setInvSlotID(invSlotID, itemNum);
						synthContainer->setQuantity(1, itemNum);

						CConsole::outDebOnly("Item: %i, Inv Slot: %i",synthContainer->getItemID(itemNum),synthContainer->getInvSlotID(itemNum));
					} else {
						synthContainer->setItemID(0, itemNum);
						synthContainer->setInvSlotID(0, itemNum);
						synthContainer->setQuantity(0, itemNum);
					}
				}
				schar->setSynth(synthContainer);
				schar->sendSynth(crystalItemID, crystalInvSlotID);
				diff = clock() - now;
				CProfilingWindow::getInstance()->setValue("genGamePacket::synth(0x96)", "%d", diff);
				break;
			}
			case 0xA2:		
				now = clock();
				size += schar->diceroll(buf+size, code[0]);
				diff = clock() - now;
				CProfilingWindow::getInstance()->setValue("genGamePacket::dicerole(0xa2)", "%d", diff);
				break;
			case 0xAA: { // Buy from guild shop
				now = clock();
				unsigned short itemID = 0;
				unsigned char quantity = 0;
				memcpy(&itemID,(pd.c+4),2);
				memcpy(&quantity,(pd.c+7),1);
				schar->sendGuildBuy(itemID, quantity);
				diff = clock() - now;
				CProfilingWindow::getInstance()->setValue("genGamePacket::(0xaa)", "%d", diff);
				break;
			}
			case 0xAB:  // Guild shop buy menu
				now = clock();
				schar->sendGuildBuyMenu();
				diff = clock() - now;
				CProfilingWindow::getInstance()->setValue("genGamePacket::(0xab)", "%d", diff);
				break;
			case 0xAC: { // Sell to guild shop
				now = clock();
				unsigned short itemID = 0;
				unsigned char invSlotID = 0x00;
				unsigned char quantity = 0x00;
				memcpy(&itemID,(pd.c+4),2);
				memcpy(&invSlotID,(pd.c+6),1);
				memcpy(&quantity,(pd.c+7),1);
				schar->sendGuildSell(itemID, invSlotID, quantity);
				diff = clock() - now;
				CProfilingWindow::getInstance()->setValue("genGamePacket::(0xac)", "%d", diff);
				break;
			}
			case 0xAD:  // Guild shop sell menu
				now = clock();
				schar->sendGuildSellMenu();
				diff = clock() - now;
				CProfilingWindow::getInstance()->setValue("genGamePacket::(0xad)", "%d", diff);
				break;
			case 0xb5:	// send tell message
				now = clock();
				size += schar->sendSay(buf+size, pd, code[0]);
				diff = clock() - now;
				CProfilingWindow::getInstance()->setValue("genGamePacket::(0xb5)", "%d", diff);
				break;
			case 0xb6:
				now = clock();
				size += schar->sendTell(buf+size, pd, code[0]);
				diff = clock() - now;
				CProfilingWindow::getInstance()->setValue("genGamePacket::(0xb6)", "%d", diff);
				break;
			case 0xc3:
			{
				now = clock();
				CLinkshell::addLinkPearl(schar);
				diff = clock() - now;
				CProfilingWindow::getInstance()->setValue("genGamePacket::(0xc3)", "%d", diff);
				break;
			}
			case 0xc4: //Linkshell operations: creation, equiping, creating pearl etc.
			{
				now = clock();
				CLinkshell::createLinkshellPacket(schar, pd.c);
				diff = clock() - now;
				CProfilingWindow::getInstance()->setValue("genGamePacket::(0xc4)", "%d", diff);
				break;
			}
			case 0xD2:
				now = clock();
				schar->sendPartyMembersMap();
				diff = clock() - now;
				CProfilingWindow::getInstance()->setValue("genGamePacket::(0xd2)", "%d", diff);
				break;
			case 0xE0:	
				schar->updateChar();
				break;
			case 0xDB:

				break;
			case 0xDC:	
				now = clock();
				size += schar->seekAnon(buf+size, pd.c, code[0]);
				diff = clock() - now;
				CProfilingWindow::getInstance()->setValue("genGamePacket::(0xdc)", "%d", diff);
				break;
			case 0xDD: {
				now = clock();
				int charid = 0;
				memcpy(&charid,(pd.c+4),4);
				CConsole::outDebOnly("Checking CharID: %i",charid);
				schar->check(charid);
				diff = clock() - now;
				CProfilingWindow::getInstance()->setValue("genGamePacket::(0xdd)", "%d", diff);
				break;
			}

			case 0xE1: //sent after linkshell is equipped/logged in with
			// use unknown, can't be used as a trigger for lsmes atm because it will crash the client on login
			// will be send when /lsmes is used without any params
			{
				//CLinkshell::sendLsMsg(schar); -- crashes the client
				//size += CLinkshell::sendLsMsg(schar, buf+size);
				now = clock();
				if (schar->lsLoginMessageSent) {
					CLinkshell::sendLsMsg(schar);
				}
				diff = clock() - now;
				CProfilingWindow::getInstance()->setValue("genGamePacket::(0xe1)", "%d", diff);
				break;
			}
			case 0xE2:		// E2 - Update ls message
			{
				now = clock();
				if ((pd.c[4]&0x40) == 0x40) { //0x40 = linkpearl/sack, 0x4C = linkshell
					char msg[115];
					memset(msg,0,115);
					memcpy(msg,(pd.c+12),114);
					CConsole::outDebOnly("New LsMes: %s",msg);
					CLinkshell * linkshell;
					linkshell = schar->getLinkshell();
					if(linkshell != NULL)
						linkshell->setLsMes(msg,schar->getName());
					CLinkshell::sendLsMsg(schar);
				}
				diff = clock() - now;
				CProfilingWindow::getInstance()->setValue("genGamePacket::(0xe2)", "%d", diff);
				break;
			}
			case 0xe7:		// allow logout
				now = clock();

				//Byte 7: 1 = logout to char list, 3 = shutdown;
				schar->m_logoutType = pd.uc[6];
				CConsole::outDeb("Logout Type: %d", schar->m_logoutType);

				size += schar->allowLogout(buf+size, code[0]);
				diff = clock() - now;
				CProfilingWindow::getInstance()->setValue("genGamePacket::(0xe7)", "%d", diff);
				break;
			case 0xE8:
				now = clock();
				switch(schar->getAnimation()) {
				case 0x21:
					schar->setAnimation(0x00);
					schar->isHealing(false);
					break;
				case 0x00:
					if(!schar->isCasting()) {
						schar->setAnimation(0x21);
					}
					schar->isHealing(true);
					break;
				}
				schar->updateChar();
				diff = clock() - now;
				CProfilingWindow::getInstance()->setValue("genGamePacket::(0xe8)", "%d", diff);
				break;
			case 0xEA:		
				now = clock();
				schar->tsit(buf+size, pd.c, code[0]);
				diff = clock() - now;
				CProfilingWindow::getInstance()->setValue("genGamePacket::(0xea)", "%d", diff);
				break;
			case 0xF2: {
				now = clock();
				unsigned short boundaryNum = 0;
				memcpy(&boundaryNum,(pd.c+6),2);
				CConsole::outDebOnly("Area Boundary: %i\n",boundaryNum);
				schar->setBoundary(boundaryNum);
				diff = clock() - now;
				CProfilingWindow::getInstance()->setValue("genGamePacket::(0xf2)", "%d", diff);
				break;
			}
			case 0x100: { // moogle job change
				now = clock();
				unsigned char mainJob = 0x00, subJob = 0x00;
				memcpy(&mainJob,pd.c+4,1);
				memcpy(&subJob,pd.c+5,1);
				if(mainJob > 0x00) {
					schar->cmjob(mainJob, schar->j.job[mainJob]);
				}
				if(subJob > 0x00) {
					schar->csjob(subJob, schar->j.job[subJob]);
				}
				diff = clock() - now;
				CProfilingWindow::getInstance()->setValue("genGamePacket::(0x100)", "%d", diff);
				break;
		    }
			default:
				CConsole::outDeb("Unhandled game packet %.2X\n",pd.us[0] & 0x1ff);
		}
	}

	schar->packetList->lock();
	CBasicPacket* currPacket = NULL;
	now = clock();
	sendInventory = false;
	while(schar->packetList->getSize()>0) {
		currPacket = schar->packetList->at(0);

		if ((!sendInventory) && ((size + ( currPacket->getSize()*2) ) > _MAX_PACKET_SIZE)) {
			CConsole::outErr("Packetlist overflow prevented! newSize: %i (char: %s)", size, schar->getName());//this should not happen
			break;
		}

		if ((!sendInventory) && ((currPacket->getType() == 0x20) || (currPacket->getType() == 0x1E) || (currPacket->getType() == 0x1F))) {
			if (size != 0) {
				break; 
			}
			sendInventory = true;
		}

		if ((sendInventory) && (currPacket->getType() == 0x1D)) {
			sendInventory = false;
		}

		

		size += CPacket::sendPremadeCPacket((buf+size),  code[0], currPacket);

		if ((currPacket->getType() != 0x0d) &&
			(currPacket->getType() != 0x0e)) {
			CConsole::outDebOnly("Preparing packet %x", currPacket->getType());
		}
		schar->packetList->removeAt(0);

		delete currPacket;
	}
	diff = clock() - now;
	packetlistpacketsMax = max(packetlistpacketsMax, diff);
	CProfilingWindow::getInstance()->setValue("genGamePacket::adding packet list packets", "%d - Max: %d", diff, packetlistpacketsMax);
	schar->packetList->unlock();
	
	union PacketData pdd;
	pdd.c = (char *)buf+size;
	unsigned int i =0;
	// cycle through the chars command list
	schar->lockCommList();
	now = clock();
	for(i = 0; i < schar->commList.size(); i++ ) {
		if(schar->commList.at(i).getComType() != 0) {
		//	CConsole::outTime("Executing Command 0x%hx",schar->commList.at(i).getComType());
			if(size > 1500) {
				break;
			}
			switch(schar->commList.at(i).getComType()) {
				case 0x1510:
					break;
				case 0x0b:
					size += schar->logout(buf+size, code[0], false);
					break;
				case 0x5d:
					size += schar->emoteRecv(buf+size, &(schar->commList.at(i)), code[0]);
					break;
				case 0xb5:
				case 0xb6:
					size += schar->recvChat((buf+size), code[0], &(schar->commList.at(i)));
					break;
				case 0x5e0c:
					size += schar->chip(buf+size, code[0]);
					break;
				case 0x101:
					size += schar->updatePCs((buf+size), &(schar->commList.at(i)) , code[0]);
					break;
				default:
					break;
			}
		}
		schar->commList.erase(schar->commList.begin()+i,schar->commList.begin()+i+1);
	}
	diff = clock() - now;
	commlistpacketsMax = max(commlistpacketsMax, diff);
	CProfilingWindow::getInstance()->setValue("genGamePacket::adding comm list packets", "%d - Max: %d", diff, commlistpacketsMax);

	schar->unlockCommList();

compress1:
	now = clock();

	//Set new server cnt
	//Bytes 1-2 Clientcount
	unsigned short* clientCount = (unsigned short *)output;
	//Bytes 3-4 Servercount
	unsigned short* serverCount = (unsigned short *)(output+2);
	*serverCount = *clientCount;

	//Bytes 5-8 Unknown

	//Bytes 9-12 Timestamp
	//write timestamp (32 BIT!)
	__time32_t* timestamp = (__time32_t*) (output+8);
//	CConsole::outDeb("Old time: %.8X", *timestamp);
	_time32(timestamp);
//	CConsole::outDeb("New time: %.8X", *timestamp);

	//compress
	size = CZlib::compress((char *)buf, size, output+0x1c, 0x524, compressTable);

	//write size
	j = (size+7)/8+0x1c;
	memcpy(output+(unsigned long)j, &size, 4);

	//calculate hash
	size = (size+7)/8+4;
	unsigned char hash[16];
	md5((unsigned char *)(output+0x1c), hash, size);
	memcpy(output+(unsigned long)size+0x1c, hash, 16);
	size += 16;
	k = (size/4)&-2;
	for(j = 0; j < k; j += 2) {
		CBlowfish::encipher((unsigned long *)output+j+7, (unsigned long *)output+j+8, schar->enc.P, schar->enc.S[0]);
	}

	diff = clock() - now;
	compressMax = max(compressMax, diff);
	CProfilingWindow::getInstance()->setValue("genGamePacket::compress", "%d - Max: %d", diff, compressMax);

	return size+0x1c;
}

int CPacket::genLobbyPacket(char * output, char * recvbuf, struct connections* r) {
	SOCKET dataS = SOCKET(r->lobbySocket);

	//struct sockaddr_in client;
	//unsigned int socksize = sizeof(client);
	unsigned long size = 0;
	
	char buf[56];
	memset(buf,0,56);
	char buf2[156];
	memset(buf2,0,156);
	char uList[300];
	memset(uList,0,300);
	unsigned char hash[16];
	MYSQL* mysqlConn;
	unsigned long  count;
	char query[16384];

	MYSQL_RES * res;
	MYSQL_ROW row;

	// Update the character
	CConsole::outTime("Incoming Packet: %X ", recvbuf[8]);
	switch(recvbuf[8]) {
		case 0x26:
		{
			size = 0x28;

			unsigned char packet1[] =  {
			0x28, 0x00, 0x00, 0x00, 0x49, 0x58, 0x46, 0x46, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4f, 0xe0, 0x5d, 0xad, 
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };

			unsigned char expansions = 0x3F;  // Bit mask for expansions

			memcpy(packet1+32,&expansions,1);

			md5(&packet1[0], hash, size);
			memcpy(packet1+12,hash,16);
			
			memcpy(output, packet1, size);
			break;
		}
		case 0x14://Delete char
		{
			CConsole::outDebOnly("Delete Char (WIP!)");
			unsigned int contentID=recvbuf[0x1C];
			contentID+=recvbuf[0x1D]<<8;
			contentID+=recvbuf[0x1E]<<16;
			contentID+=recvbuf[0x1F]<<24;
			CConsole::outDebOnly("Content ID: %.8X (%d)",contentID,contentID);
			unsigned int charID=recvbuf[0x20];
			charID+=recvbuf[0x21]<<8;
			charID+=recvbuf[0x22]<<16;
			charID+=recvbuf[0x23]<<24;
			CConsole::outDebOnly("Char ID: %.8X (%d)",charID,charID);

			size = 0x20;

			unsigned char packet1[] =  {
			0x20, 0x00, 0x00, 0x00, 0x49, 0x58, 0x46, 0x46, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00 };

			md5(&packet1[0], hash, size);
			memcpy(packet1+12,hash,16);

			int iResult = send(r->polSocket, (char*)&packet1[0], size, 0);
			CConsole::outDebOnly("Delete Char send result: %d",iResult);

			CChar::deleteChar(charID);

			//Do packet 0x1f next
		}
		case 0x1f:
		{
			int timeout = 0;
			while(((unsigned char)buf[0] != 0xa1) && timeout < 20) {
				buf[0]= 0x01;
				send(dataS, buf, 5 ,0 );
				Sleep(700);
				recv(dataS, buf, 40 ,0);
				timeout++;
			}
			if (timeout >= 20) {
				return -1;
			}
		
			memcpy(&r->acctID, buf+1,4);

			memcpy((char *)&(r->servIP), buf+5,4);
			
	
			unsigned char charList[2500];

			memset(charList, 0, 2500);
			unsigned long * lengths;
			int i = 0;
			charList[0] = 0xe0;
			charList[1] = 0x08;

			charList[4] = 0x49;
			charList[5] = 0x58;
			charList[6] = 0x46;
			charList[7] = 0x46;

			charList[8] = 0x20;
			charList[28] = 0x10;

			sprintf(query, "SELECT charid FROM chars WHERE acctid = %i",r->acctID);
			count = (int) strlen(query);
			strcpy(query+count, " ORDER BY charid ASC");
			count = (int) strlen(query);

			mysqlConn=CDb::getConn(); //get conn
			mysql_real_query(mysqlConn, query, count);

			res = mysql_store_result(mysqlConn);
			CDb::pushConn(mysqlConn); //release conn

			unsigned char packet2[] = {
			0xc4, 0x01, 0x00, 0x00, 0x49, 0x58, 0x46, 0x46, 0x20, 0x00, 0x00, 0x00, 0x2a, 0x72, 0x4a, 0x94, 
			0x4f, 0x60, 0x27, 0xc4, 0x45, 0x4b, 0x7d, 0xcf, 0x27, 0x8e, 0x6d, 0xcd, 0x03, 0x00, 0x00, 0x00, 

			0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x56, 0x61, 0x6c, 0x65, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x05, 0x00,
			0x07, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x02, 0x00, 0x10, 0x00, 0x20, 0x00, 0x30,
			0x00, 0x40, 0x00, 0x50, 0x00, 0x60, 0x00, 0x70, 0x00, 0x01, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00,
			0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb5, 0xfa, 0x01, 0x00,
			0x7e, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
			0x01, 0x01, 0x01, 0x01, 0x46, 0x6e, 0xcf, 0x09, 0xde, 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x0a, 0x52, 0x03, 0x00, 0x0e, 0x08, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00 };

			memcpy(packet2+60, CGlobal::serverName, 16);

			for(int j = 0; j<16; j++) {
				memcpy(charList+32+140*j, packet2+32, 140);
				char contentid[3];
				memcpy(&contentid, charList+32, 3);
				contentid[2] += j;
				memcpy(charList+32+140*j, &contentid, 3);
				memcpy(uList+16*(j+1),&contentid, 3);
				int cid;
				memcpy(&cid, &contentid, 3);
				
			}

			if(!res) {
				CConsole::outErr("no chars found!");
			} else {
				uList[0] = 0x03;

				CChar* tempChar = new CChar();
					
				while((row = mysql_fetch_row(res))) {
					lengths = mysql_fetch_lengths(res);

					unsigned int charid = atoi(row[0]);
					memset(tempChar->m_name,0,17);

					if(!tempChar->loadLook(charid, false)) {
						CConsole::outErr("Character not found!");
						break;
					}

					memcpy(uList+20*(i+1), &charid, sizeof(unsigned int));

					memcpy(charList+32+i*140+4, &charid, sizeof(unsigned int));
					memcpy(charList+32+i*140+12, tempChar->m_name, 16);
					
					charList[46+32+i*140] = tempChar->m_jobMain;
					charList[56+32+i*140] = tempChar->l.face;
					charList[73+32+i*140] = tempChar->m_lvlMain;

					charList[32+72+i*140] = tempChar->getDisplayZone();

					memcpy(charList+58+32+i*140, &tempChar->l.head, 2);
					memcpy(charList+60+32+i*140, &tempChar->l.body, 2);
					memcpy(charList+62+32+i*140, &tempChar->l.hands, 2);
					memcpy(charList+64+32+i*140, &tempChar->l.legs, 2);
					memcpy(charList+66+32+i*140, &tempChar->l.feet, 2);

					memcpy(charList+68+32+i*140, &tempChar->l.main, 2);
					memcpy(charList+70+32+i*140, &tempChar->l.sub, 2);

					charList[44+32+i*140] = tempChar->l.race;
					i++;
				}
				delete tempChar;

				uList[1] = 0x10;
				send(dataS, uList, 0x110,0);
			}
			mysql_free_result(res);

			md5((unsigned char*)(charList), hash, 2272);
			memcpy(charList+12,hash,16);
	
			size = 2272;
		
			memcpy(output, charList, 2272);
			break;
		}
		case 0x24:
		{
			unsigned char packet3[] = {
			0x40, 0x00, 0x00, 0x00, 0x49, 0x58, 0x46, 0x46, 0x23, 0x00, 0x00, 0x00, 0xbf, 0xf5, 0x47, 0xa4, 
			0x4e, 0x62, 0xcd, 0x9e, 0x92, 0xe7, 0x72, 0x26, 0xea, 0x9f, 0x4e, 0x98, 0x20, 0x00, 0x00, 0x00, 
			0x64, 0x00, 0x00, 0x00, 0x70, 0x58, 0x49, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
			
			memcpy(packet3+36,CGlobal::serverName,16);
			memset(packet3+12,0,16);
			md5((unsigned char*)(packet3), hash, 64);
			memcpy(packet3+12,hash,16);
			size = 64;
			memcpy(output, packet3, 64);

			break;
		}
		case 0x07:
		{
			char query[500];
			memset(query, 0, 500);
			unsigned char key[] = {0x58,0xe0,0x5d,0xad};
			unsigned char packet4[] = {
			0x48, 0x00, 0x00, 0x00, 0x49, 0x58, 0x46, 0x46, 0x0b, 0x00, 0x00, 0x00, 0x30, 0xd0, 0x10, 0xdc, 
			0x87, 0x64, 0x4b, 0x34, 0x72, 0x9a, 0x51, 0x23, 0x54, 0x14, 0x67, 0xf0, 0x82, 0xb2, 0xc0, 0x00, 
			0xc3, 0x57, 0x00, 0x00, 0x52, 0x65, 0x67, 0x69, 0x75, 0x7a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
			0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x01, 0xd6, 0xd3, 0x00, 0x00, 
															0x7f, 0x00, 0x00, 0x01, 0xf2, 0xd2, 0x00, 0x00 };

			while ((unsigned char)buf[0] != 0xA2) {
					buf[0] = 0x02;
				send(dataS, buf, 5 ,0 );
				while (recv(dataS, buf, 40 ,0)<1);
			}

			int key3[5];

			memset(key3, 0, 20);
			memcpy(key3,buf+1,20);

			memcpy(packet4+56, &r->servIP, 4);
			memcpy(packet4+64, &r->servIP, 4);

			unsigned int charid;
			memcpy((char *)&(charid),recvbuf+32,sizeof(unsigned int));

			// Check to see if the character is already logged in
			CChar* tempChar = CGlobal::getCCharById(charid);
			if(tempChar!=NULL) {
				CConsole::outTime("Duplicate login older Char %s is removed.",tempChar->m_name);

				CGlobal::waitCharList();
				// Make removal known to other clients
				//CComm comm;
				//comm.setComType(0x101);
				//comm.ptr = (LPVOID *)tempChar;
				//tempChar->insertComm(CHAR_INZONE, comm, true);
				tempChar->insertPacket(CHAR_INZONE,CCharPacket(tempChar,false));

				// Force leave a party; no matter what
				tempChar->forcePartyLeave();

				if(tempChar->hasTreasurePool()) {
					tempChar->getTreasurePool()->removeMember(tempChar);
				}

				// Remove the char from linkshell
				if(tempChar->getLinkshell()!=NULL) {
					tempChar->getLinkshell()->removeOnlineMember(tempChar);
				}

				tempChar->loggedOut=true;

				CGlobal::removeCChar(tempChar);

				if(tempChar->loggedIn) {
					CZoneHandler::getInstance()->decreaseZoneCounter(tempChar->loc.zone);
				}

				CGlobal::exitCharList();

				//Push the char onto the removal list
				CGlobal::waitCharRemovalList();
				CGlobal::charRemovalList.push_back(tempChar);
				CGlobal::exitCharRemovalList();
			}

			CConsole::outDebOnly("Loading Char: %d",charid);

			CChar* schar = new CChar();

			memset(&(schar->st),0,sizeof(CStats));
			schar->pHP = 0;
			schar->pMP = 0;
			memset(&(schar->h),0,sizeof(struct health));
			memset(&(schar->stats),0,sizeof(struct stats));
			schar->m_modDef = 0;
			
			if(!schar->loadChar(charid, true)) {
				CConsole::outErr("Character not found!");
				return 0;
			}

			schar->clientConn.acctID = r->acctID;
			schar->clientConn.acctSocket = r->acctSocket;
			schar->clientConn.lobbySocket = r->lobbySocket;
			schar->clientConn.outSocket = r->outSocket;
			schar->clientConn.polSocket = r->polSocket;
			schar->clientConn.servIP = r->servIP;
			schar->clientConn.clientIP = r->clientIP;
			
			// Check to see if the character is already logged in
			/*
			unsigned int k = 0;
			CChar* tempChar;

			CGlobal::waitCharList();
			while(k < CGlobal::getCCharListSize()) 
			{
				tempChar=CGlobal::getCCharAt(k);
				CConsole::outDebOnly("Duplicate Check: %d/%d: %s",k+1,CGlobal::getCCharListSize(),tempChar->m_name);
				
				// Check to see if our character is logged on
				if (((tempChar->m_id == schar->m_id) && (tempChar->clientConn.acctID == schar->clientConn.acctID)) && tempChar->lastUpdate < schar->lastUpdate) 
				{
					CConsole::outTime("Duplicate login older Char %s is removed.",tempChar->m_name);

					// Make removal known to other clients
					CComm comm;
					comm.setComType(0x101);
					comm.ptr = (LPVOID *)tempChar;
					tempChar->insertComm(CHAR_INZONE, comm, true);

					//Force the player to leave the party
					tempChar->forcePartyLeave();

					// Remove the character
					CGlobal::removeCChar(tempChar);

					if (tempChar->loggedIn)
						CZoneHandler::decreaseZoneCounter(tempChar->getZone());

					CGlobal::waitCharRemovalList();
					tempChar->loggedOut=true;//this currently allows fleeing from battle by logging out, needs a fix
					CGlobal::charRemovalList.push_back(tempChar);
					CGlobal::exitCharRemovalList();
				}
				else
				{
					k++;
				}
			}
			CGlobal::exitCharList();
			*/

			if(schar->firstlogin == 0x01 && key3[3] == 0x00000000) {
				unsigned char key[] = {0x54,0xe0,0x5d,0xad};
				memset(key3, 0, 20);
				memcpy(&key3[4], key, 4);
				key3[4] += 10;

				CConsole::outTime("FIRST LOGIN!");
				sprintf(query, "UPDATE chars SET firstlogin = 0x00 WHERE charid = %i",schar->m_id);
				count = (int) strlen(query);

				MYSQL* mysqlConn=CDb::getConn(); //get conn
				mysql_real_query(mysqlConn, query, count);
				CDb::pushConn(mysqlConn); //release conn
			}

			int i;
			for(i = 0; i <= 4; i++) {
				CConsole::outDebOnly(" %X ", key3[i]);
			}
			
			schar->zoning = 0; // may be wrong
			schar->lastUpdate = (unsigned long long) clock();

			memcpy((schar->enc.key), key3, 20);
			md5((unsigned char *)schar->enc.key, hash, 20);
			
			
			for(i = 0; i < 16; i++) {
				if(hash[i] == 0) {
					break;
				}
			}
			memset(hash+i, 0, 16-i);
			memcpy(schar->enc.hash, hash, 16);
			
			
			CBlowfish::init((char *)schar->enc.hash, 16, schar->enc.P, schar->enc.S[0]);

		
			memset(packet4+12,0,16);

			md5((unsigned char*)(packet4), hash, 72);

			memcpy(packet4+12,hash,16);
			memcpy(output, packet4, 72);

			//increase the zone counter for the zone we logged in
			CZoneHandler::getInstance()->increaseZoneCounter(schar->getZone());

			schar->loggedIn = true;

			CGlobal::addCChar(schar);

			size = 72;
			break;
		}
		case 0x22: // new char part 1
		{
			//schar = new CChar();
			char name[16];
			//memcpy(name, recvbuf+32, 16);
			memset(name,0,16);
			memcpy(name, recvbuf+32, 16);
			name[15]='\0';

			sprintf(query, "SELECT charname FROM chars WHERE charname LIKE '%s'",name);

			//CConsole::outDebOnly("Query: %s",query);

			MYSQL* mysqlConn=CDb::getConn(); //get conn
			mysql_real_query(mysqlConn, query, (unsigned long)strlen(query));
			res = mysql_store_result(mysqlConn);
			CDb::pushConn(mysqlConn); //release conn

			unsigned char pSize=0x24;
			unsigned char packet5[0x24];

			if((res) && (mysql_num_rows(res)>0)) {
				CConsole::outErr("Charactername '%s' already taken.",name);
				unsigned char packet[] = {
				0x24, 0x00, 0x00, 0x00, 0x49, 0x58, 0x46, 0x46, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00,
				0x39, 0x01, 0x00, 0x00};

				memcpy(&packet5[0],&packet[0],pSize);
			} else {

				memcpy(r->newCharName, name, 16);
			
				pSize=0x20;
				unsigned char packet[] = {
				0x20, 0x00, 0x00, 0x00, 0x49, 0x58, 0x46, 0x46, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00 };

				memcpy(&packet5[0],&packet[0],pSize);
			}

			md5((unsigned char*)(packet5), hash,pSize);
			memcpy(packet5+12,hash,16);
			memcpy(output, packet5, pSize);
			size = pSize;
			break;
		}
		case 0x21: // new char part 2
		{
			char newu[] = { 0x15, 0x07 };
			send(dataS, newu, 2 ,0);

			CChar* schar = new CChar();
			memcpy(schar->m_name,r->newCharName,16);

			schar->clientConn.acctID = r->acctID;
			schar->clientConn.acctSocket = r->acctSocket;
			schar->clientConn.lobbySocket = r->lobbySocket;
			schar->clientConn.outSocket = r->outSocket;
			schar->clientConn.polSocket = r->polSocket;
			schar->clientConn.servIP = r->servIP;
			schar->clientConn.clientIP = r->clientIP;

			memset(&schar->l,0,sizeof(struct look));

			memcpy(&(schar->l.race),  recvbuf+48,1);

			memcpy(&(schar->l.face),  recvbuf+60,1);
			memcpy(&(schar->m_jobMain), recvbuf+50,1);
			memcpy(&(schar->st.n.home), recvbuf+54,1);

			memset(schar->st.n.rank,0,3);
			schar->st.n.rank[0] = 1;
			schar->st.n.rank[1] = 1;
			schar->st.n.rank[2] = 1;

			//memset(schar->m_name,0,16);
			//memcpy(schar->m_name, name, 16);

			schar->m_lvlMain = 1;

			schar->l.head  = 0x00;
			schar->l.body  = 0x08;
			schar->l.hands = 0x08;
			schar->l.legs  = 0x08;
			schar->l.feet  = 0x08;

			schar->l.main  = 0x00;
			schar->l.sub   = 0x00;


			CStats::calcStats(schar);

			schar->h.flags = 0x00;
			schar->setAnimation(0x00);
	
			schar->h.hp = schar->getMaxHp();
			schar->h.mp = schar->getMaxMp();
			schar->h.hpp = 0x64;
			if(schar->getMp() == 0) {
				schar->h.mpp = 0x00;
			} else {
				schar->h.mpp = 0x64;
			}

			memset(&schar->j,0,sizeof(struct jobs));
			schar->j.unlocked = 0x7e;
			schar->j.war = 1;
			schar->j.whm = 1;
			schar->j.blm = 1;
			schar->j.thf = 1;
			schar->j.rdm = 1;
			schar->j.mnk = 1;

			schar->loc.prevzone = 0x00;
			//Failsafe initialization of pos, now you can't have an out-of-bounds HP.
			schar->loc.p.x = 0;
			schar->loc.p.y = 0;
			schar->loc.p.z = 0;

			// subject to change
			switch((unsigned int)schar->st.n.home) {
				case 0x02: // windy start
					schar->setZone(0xee+rand()%3);
					//do not allow windy walls as startzone.
					while(schar->getZone() == 0xef) {
						schar->setZone(0xee+rand()%3);
					}
					schar->st.homepoint = (unsigned char)schar->getZone();
					break;
				case 0x01: // bastok start
					schar->setZone(0xEA+rand()%2);
					schar->st.homepoint = (unsigned char)schar->getZone();
					break;
				case 0x00: // sandy start
					schar->setZone(0xE6+rand()%2);
					schar->st.homepoint = (unsigned char)schar->getZone();
					break;
			}

			memset(&schar->s,0,sizeof(struct skills));

			schar->setGil(0);

			schar->inventory->setSize(30);
			schar->mogsafe->setSize(50);
			schar->storage->setSize(0);
			schar->moglocker->setSize(0);

			// memcpy((char *)&(schar->acctid), &r->ip, 4);


			for(int i=0; i<17; i++) {
				schar->equip[i] = 0x00;	
			}

			memset(schar->k.keyList,0,0x100);
			memset(schar->k.seenList,0,0x100);

			memset(schar->sp.spellList,0,0x100);

			schar->fameSan = 0;
			schar->fameBas = 0;
			schar->fameWin = 0;
			schar->fameNorg = 0;

			strcpy(query, "SELECT MAX(charid) FROM chars");

			MYSQL* mysqlConn=CDb::getConn(); //get conn
			mysql_real_query(mysqlConn, query, (unsigned long)strlen(query));

			res = mysql_store_result(mysqlConn);
			CDb::pushConn(mysqlConn); //release conn

			unsigned int charid = 0x5544;
			if(res) {
				if(mysql_num_rows(res)) {
					row = mysql_fetch_row(res);
					if(row[0]) {
						charid= atoi(row[0]);
						charid++;
					}
				}
				mysql_free_result(res);
			}	

			schar->setId(charid);
			schar->setObjType(OBJ_PC);

			CLuaManager::OnCharCreate(schar);

			memset(schar->enc.key, 0 ,20);

			schar->createChar();

			delete schar;

			unsigned char packet6[] = {
			0x20, 0x00, 0x00, 0x00, 0x49, 0x58, 0x46, 0x46, 0x03, 0x00, 0x00, 0x00, 0xc8, 0xa0, 0x48, 0xb9, 
			0xb9, 0xa8, 0xd2, 0xdb, 0x4b, 0x55, 0xcc, 0x03, 0x54, 0x00, 0x8d, 0xe0, 0x20, 0x00, 0x00, 0x00 };
			memset(packet6+12,0,16);
			md5((unsigned char*)(packet6), hash, 32);
			
			memcpy(packet6+12,hash,16);
			memcpy(output, packet6, 32);
			size = 32;
			break;
		}
		default:
		{
			char message[256];

			CConsole::outDeb("Unhandled Lobby Packet %.2X",recvbuf[8]);
			unsigned int packetsize = *((unsigned int*)&recvbuf[0]);

			sprintf(message," ");
			for(unsigned int y=0; y < packetsize; y++) {
				sprintf(message,"%s %02hx ",message,(unsigned char)recvbuf[y]);
				if(((y+1)%16) == 0) {
					CConsole::outPlain(message);
					sprintf(message,"");
				}
			}
			if (strlen(message)>0) {
				CConsole::outPlain(message);
				sprintf(message,"");
			}
			return -1;
		}
	}
	Sleep(500);
	if (size > 0 ) {
		return size;
	}
	else return 0;
}


int CPacket::loadPacket(char * filename, unsigned char * buf, unsigned short num){
	FILE * fp;
	unsigned int size, i;

	fp = fopen(filename, "rb");
	if(!fp) {
		return 0;
	}
	fseek(fp, 0, SEEK_END);
	size = ftell(fp);
	rewind(fp);
	fread(buf, sizeof(char), size, fp);
	fclose(fp);

	for(i = 0; i < size && buf[i+1]; i += (unsigned int)buf[i+1]*2) {
		memcpy(buf+i+2, &num, sizeof(num));
	}

	return size;
}

int CPacket::loadPacket2(char * filename, unsigned char * buf, unsigned short num, int ani){
	FILE * fp;
	unsigned int size;

	fp = fopen(filename, "rb");
	if(!fp) {
		return 0;
	}
	fseek(fp, 0, SEEK_END);
	size = ftell(fp);
	CConsole::outTime(" %i ", size);
	rewind(fp);
	fread(buf, sizeof(char), size, fp);
	fclose(fp);

	memcpy(buf+2, &num, sizeof(num));
	memcpy(buf+2+0x42, &num, sizeof(num));

	buf[0x1f] = ani;

	return size;
}

int CPacket::sendPremadePacket(unsigned char * data, unsigned short num, CComm * comm) {

	memcpy(data, comm->message, comm->charid);
	memcpy(data+2, &num, sizeof(num));

	return comm->charid;
}

int CPacket::sendPremadeCPacket(unsigned char * data, unsigned short num, CBasicPacket *packet) 
{
	packet->write(data);
	memcpy(data+2, &num, sizeof(num));

	return packet->getSize()*2;
}

int CPacket::sendServerTell( char* from, char* to, char* text){

 	char name[16];
	memcpy(name,to,15);
	unsigned char message[255];
	memset(message,0,255);
	union PacketData pd;

	pd.uc = message;
	CComm comm;

	memcpy(comm.fromc, from, 16);
	
	memcpy(pd.c+5,from,strlen(from));

	
	comm.pData = pd;
	int test = (int) strlen(text)*2+20;
	if (test > 255) {
		CConsole::outTime("BAD SERVER TELL SIZE!  %d",test);
		return 0;
	}
	memcpy(&comm.lenght,&test,1);
	memset(comm.message,0,255);
	memcpy(comm.message,text,strlen(text));

	comm.ident = 0x03;

	CConsole::outDebOnly("%s %x %x %s", comm.message,strlen(text),comm.lenght,text);


	CChar *t;// = new CChar();
	t = CChar::getCChar(0, name, 0);

	if(t) {
		comm.setComType(0xb6);
		t->addCommList(comm);
	}


	return 0;
}

/*
//Gets values off a packed buffer and returns a pointer to the unpacked values ( shifts the inbuffer )
void CPacket::getPackedValues(byte* inbuf, int size, int num, byte *outbuf) {
	memset(outbuf,0,10);
	byte intmp[100];
	memset(intmp,0,100);
	memcpy(intmp,inbuf,num+5);

	byte tmp,tmp2;

	for(int k = 0; k < num; k+=1) {
		tmp2 = 0;
		tmp = (intmp[k] << size);
		intmp[k] >>= 8-size;
		for(int i = k; i < num; i+=1) {
			tmp2 = intmp[i+1] << size;
			intmp[i+1] >>= 8-size;
			intmp[i+1] |= tmp;
			tmp = tmp2;
		}
	}

	memcpy(outbuf,intmp,num);
	memcpy(inbuf,inbuf+num-1, num);

	for(int i = 0; inbuf[i] != 0; i++) {
			tmp = inbuf[i+1] >> 7;
			inbuf[i] <<= 1 ;
			inbuf[i] |= tmp;
		}
}
*/
